﻿#ifndef PACKUTILS_H
#define PACKUTILS_H

#include "protocol.h"
#include <vector>

/**
 * 获取文件名的起始长度，根据是否包含特性ID处理
 * @brief getFileNameStart
 * @param hasFeatureId
 * @return
 */
static int getFileNameStart(bool hasFeatureId) {
  if (hasFeatureId) {
    return FUTURE_ID_COUNT + HEAD_COUNT + CMD_COUNT + TOTAL_PACKAGE_BYTE_COUNT +
           PACKAGE_BYTE_COUNT;
  } else {
    return HEAD_COUNT + CMD_COUNT + TOTAL_PACKAGE_BYTE_COUNT +
           PACKAGE_BYTE_COUNT;
  }
}

/**
 * 获取文件名结束位置
 * @brief getFileNameEnd
 * @param hasFeatureId
 * @param fileNameLength
 * @return
 */
static int getFileNameEnd(bool hasFeatureId) {
  return getFileNameStart(hasFeatureId) + FILE_NAME_COUNT;
}

/**
 * 获取文件包标签起始位置
 * @brief getFileTagStart
 * @return
 */
static int getFileTagStart(bool hasFeatureId, int fileNameLength) {
  return getFileNameEnd(hasFeatureId) + fileNameLength;
}

/**
 * 获取文件包标签真正拷贝的数据位置，包含前面的标签长度2字节
 * @brief getFileTagEnd
 * @param hasFeatureId
 * @param fileNameLength
 * @return
 */
static int getFileTagEnd(bool hasFeatureId, int fileNameLength) {
  return getFileTagStart(hasFeatureId, fileNameLength) + TAG_COUNT;
}

/**
 *
 * 获取文本标签起始位置
 * @brief getTagStart
 * @param hasFeatureId
 * @return
 */
static int getTagStart(bool hasFeatureId) {
  if (hasFeatureId) {
    return FUTURE_ID_COUNT + HEAD_COUNT + CMD_COUNT + TOTAL_PACKAGE_BYTE_COUNT +
           PACKAGE_BYTE_COUNT;
  } else {
    return HEAD_COUNT + CMD_COUNT + TOTAL_PACKAGE_BYTE_COUNT +
           PACKAGE_BYTE_COUNT;
  }
}

/**
 *
 * 获取文本标签结束位置，包含标签的2字节长度
 * @brief getTagStart
 * @param hasFeatureId
 * @return
 */
static int getTagEnd(bool hasFeatureId) {
  if (hasFeatureId) {
    return FUTURE_ID_COUNT + HEAD_COUNT + CMD_COUNT + TOTAL_PACKAGE_BYTE_COUNT +
           PACKAGE_BYTE_COUNT + TAG_COUNT;
  } else {
    return HEAD_COUNT + CMD_COUNT + TOTAL_PACKAGE_BYTE_COUNT +
           PACKAGE_BYTE_COUNT + TAG_COUNT;
  }
}

/**
 * 获取文件数据包有效内容起始位置
 * @brief getFilePackageContentStart
 * @param fileNameLength
 * @param tagSize
 * @return
 */
static int getFilePackageContentStart(bool hasFeatureId, int fileNameLength,
                                      int tagSize) {
  return getFileTagEnd(hasFeatureId, fileNameLength) + tagSize;
}

/**
 * 获取文件传输包 CRC起始位置
 * @brief getFileCrcStart
 * @param hasFeatureId
 * @param fileNameLength
 * @param tagSize
 * @param packageLength
 * @return
 */
static int getFileCrcStart(bool hasFeatureId, int fileNameLength, int tagSize,
                           int packageLength) {
  return getFilePackageContentStart(hasFeatureId, fileNameLength, tagSize) +
         packageLength;
}

/**
 * 获取文件传输包魔数起始位置
 * @brief getFileExtStart
 * @param hasFeatureId
 * @param fileNameLength
 * @param tagSize
 * @param packageLength
 * @return
 */
static int getFileExtStart(bool hasFeatureId, int fileNameLength, int tagSize,
                           int packageLength) {
  return getFileCrcStart(hasFeatureId, fileNameLength, tagSize, packageLength) +
         CRC_COUNT;
}

/**
 * 获取文本传输包有效内容起始位置
 * @brief getPackageContentStart
 * @param hasFeatureId
 * @param tagSize
 * @return
 */
static int getPackageContentStart(bool hasFeatureId, int tagSize) {
  return getTagEnd(hasFeatureId) + tagSize;
}

/**
 * 获取文本传输包 CRC 起始位置，不包含2字节长度
 * @brief getCrcStart
 * @param hasFeatureId
 * @param tagSize
 * @param packageLength
 * @return
 */
static int getCrcStart(bool hasFeatureId, int tagSize, int packageLength) {
  return getPackageContentStart(hasFeatureId, tagSize) + packageLength;
}

/**
 * 获取文本传输包魔数起始位置，不包含3字节
 * @brief getExtStart
 * @param hasFeatureId
 * @param tagSize
 * @param packageLength
 * @return
 */
static int getExtStart(bool hasFeatureId, int tagSize, int packageLength) {
  return getCrcStart(hasFeatureId, tagSize, packageLength) + CRC_COUNT;
}

/**
 * 获取随机文件标签起始位置
 * @brief getRandomFileTagEnd
 * @return
 */
static int getRandomFileTagStart() {
  return HEAD_COUNT + CMD_COUNT + TOTAL_PACKAGE_BYTE_COUNT + PACKAGE_BYTE_COUNT;
}

/**
 * 获取随机文件标签结束位置
 * @brief getRandomFileTagEnd
 * @param tagSize
 * @return
 */
static int getRandomFileTagEnd() { return getRandomFileTagStart() + TAG_COUNT; }

/**
 * 获取随机文件数据包有效内容起始位置
 * @brief getRandomFilePackageContentStart
 * @param tagSize
 * @return
 */
static int getRandomFilePackageContentStart(int tagSize) {
  return getRandomFileTagEnd() + tagSize;
}

/**
 * 获取随机文件传输包CRC起始位置
 * @brief getRandomFileCrCStart
 * @param tagSize
 * @param packageLength
 * @return
 */
static int getRandomFileCrCStart(int tagSize, int packageLength) {
  return getRandomFilePackageContentStart(tagSize) + packageLength;
}

/**
 * 获取文件传输包有效长度
 * @brief getRequestBodyLength
 * @param fileNameLength
 * @param tagSize
 * @return
 */
static int getFileRequestBodyLength(bool hasFeatureId, int fileNameLength,
                                    int tagSize) {
  if (hasFeatureId) {
    return ALLOW_LENGTH -             /* 完整包长1024字节 */
           FUTURE_ID_COUNT -          /* 特性ID长度1字节 */
           HEAD_COUNT -               /* 包头长度1字节 */
           CMD_COUNT -                /* 命令长度1字节 */
           TOTAL_PACKAGE_BYTE_COUNT - /* 总包长4字节 */
           PACKAGE_BYTE_COUNT -       /* 当前包内容长度2字节*/
           FILE_NAME_COUNT -          /* 文件名长度2字节 */
           fileNameLength -           /* 文件名长度n字节 */
           TAG_COUNT -                /* 标签长度2字节 */
           tagSize -                  /* 标签内容长度n字节 */
           CRC_COUNT -                /* CRC长度2字节 */
           MAGIC_NUMBER_COUNT;        /* 魔数3字节 */
  }

  return ALLOW_LENGTH -             /* 完整包长1024字节 */
         HEAD_COUNT -               /* 包头长度1字节 */
         CMD_COUNT -                /* 命令长度1字节 */
         TOTAL_PACKAGE_BYTE_COUNT - /* 总包长4字节 */
         PACKAGE_BYTE_COUNT -       /* 当前包内容长度2字节*/
         FILE_NAME_COUNT -          /* 文件名长度2字节 */
         fileNameLength -           /* 文件名长度n字节 */
         TAG_COUNT -                /* 标签长度2字节 */
         tagSize -                  /* 标签内容长度n字节 */
         CRC_COUNT -                /* CRC长度2字节 */
         MAGIC_NUMBER_COUNT;        /* 魔数3字节 */
}

/**
 * 获取文本传输包有效内容长度
 * @brief getRequestBodyLength
 * @param tagSize
 * @return
 */
static int getRequestBodyLength(bool hasFeatureId, int tagSize) {
  if (hasFeatureId) {
    return ALLOW_LENGTH -             /* 完整包长1024字节 */
           FUTURE_ID_COUNT -          /* 特性ID长度1字节 */
           HEAD_COUNT -               /* 包头长度1字节 */
           CMD_COUNT -                /* 命令长度1字节 */
           TOTAL_PACKAGE_BYTE_COUNT - /* 总包长4字节 */
           PACKAGE_BYTE_COUNT -       /* 当前包内容长度2字节*/
           TAG_COUNT -                /* 标签长度2字节 */
           tagSize -                  /* 标签内容长度n字节 */
           CRC_COUNT -                /* CRC长度 */
           MAGIC_NUMBER_COUNT;        /* 魔数3字节 */
  }
  return ALLOW_LENGTH -             /* 完整包长1024字节 */
         HEAD_COUNT -               /* 包头长度1字节 */
         CMD_COUNT -                /* 命令长度1字节 */
         TOTAL_PACKAGE_BYTE_COUNT - /* 总包长4字节 */
         PACKAGE_BYTE_COUNT -       /* 当前包内容长度2字节*/
         TAG_COUNT -                /* 标签长度2字节 */
         tagSize -                  /* 标签内容长度n字节 */
         CRC_COUNT -                /* CRC长度 */
         MAGIC_NUMBER_COUNT;        /* 魔数3字节 */
}

/**
 * 获取随机文件长度
 * @param tagSize
 * @return
 */
static long getRandomFileRequestBodyLength(int tagSize) {
  return ALLOW_LENGTH -             /*包长，一般位1024字节*/
         HEAD_COUNT -               /* 包头1字节 */
         CMD_COUNT -                /* 命令1字节 */
         TOTAL_PACKAGE_BYTE_COUNT - /* 总包长4字节 */
         PACKAGE_BYTE_COUNT -       /* 当前包长2字节*/
         FILE_POS_COUNT -    /* 该包在文件的位置，从头开始 */
         TAG_COUNT -         /* 标签长度2字节*/
         tagSize -           /* 标签内容长度n字节*/
         CRC_COUNT -         /* CRC16 2字节 */
         MAGIC_NUMBER_COUNT; /* 魔数3个字节 */
}

/**
 * 计算总包个数
 * @param contentLength         数据总长度
 * @param requestBodyLength     单包最大有效长度
 * @return
 */
static long getTotalPackCount(long contentLength, int requestBodyLength) {
  // 文件个数
  long packageCount = contentLength / requestBodyLength;
  // 最后一个包剩余数
  long remainCount = contentLength % requestBodyLength;
  if (remainCount > 0) {
    packageCount += 1;
  }
  return packageCount;
}

/**
 * 填充文件头数据
 * @param dst             保存的容器
 * @param contentLength   文件长度
 * @param packageLength    当前包有效长度
 * @param fileNameLength  文件名长度
 */
static void fillFileHead(std::vector<char> &dst, long contentLength,
                         int packageLength, int fileNameLength) {
  dst.push_back(FUTURE_ID); // byte 0
  dst.push_back(HEAD);      // byte 1
  dst.push_back(CMD_FILE);  // byte 2

  // 1. 计算总包长度
  dst.push_back((char)contentLength);         // byte 3
  dst.push_back((char)(contentLength >> 8));  // byte 4
  dst.push_back((char)(contentLength >> 16)); // byte 5
  dst.push_back((char)(contentLength >> 24)); // byte 6

  // 2. 计算包长
  dst.push_back((char)packageLength);        // byte 7
  dst.push_back((char)(packageLength >> 8)); // byte 8

  // 3. 文件名长度
  dst.push_back((char)fileNameLength);        // byte 9
  dst.push_back((char)(fileNameLength >> 8)); // byte 10
}

/**
 * 填充文本文件长度
 * @param dst
 * @param cmd               命令
 * @param contentLength     包总长度
 * @param requestBodyLength 当前包有效长度
 * @param tagLength         标签长度
 */
static void fillHead(std::vector<char> &dst, uint8_t cmd, long contentLength,
                     int requestBodyLength, int tagLength) {
  dst.push_back((char)FUTURE_ID); // byte 0
  dst.push_back(HEAD);            // byte 1
  dst.push_back(cmd);             // byte 2

  // 1. 计算总包长
  dst.push_back(contentLength);               // byte 3
  dst.push_back((char)(contentLength >> 8));  // byte 4
  dst.push_back((char)(contentLength >> 16)); // byte 5
  dst.push_back((char)(contentLength >> 24)); // byte 6

  dst.push_back((char)requestBodyLength);        // byte 7
  dst.push_back((char)(requestBodyLength >> 8)); // byte 8

  // 标签长度
  dst.push_back(tagLength);      // byte 9
  dst.push_back(tagLength >> 8); // byte 10
}

#endif // PACKUTILS_H
